package at.ac.tuwien.prog.parser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import at.ac.tuwien.prog.Util;
import at.ac.tuwien.prog.command.CommandConstants;
import at.ac.tuwien.prog.command.ICommand;
import at.ac.tuwien.prog.command.factory.ICommandFactory;
import at.ac.tuwien.prog.entity.Register;
import at.ac.tuwien.prog.entity.Software;

/**
 * 
 * @author christoph.pickl@student.tuwien.ac.at
 */
public class ParserImpl implements IParser {

	private static final Log LOG = LogFactory.getLog(ParserImpl.class);
	
	
	private final ICommandFactory commandFactory;
	
	
	public ParserImpl(final ICommandFactory commandFactory) {
		this.commandFactory = commandFactory;
	}
	
	
	public Software parseFile(File input) throws IOException {
		assert(input.exists());
		LOG.info("parseFile(input="+input.getAbsolutePath()+")");
		
		final StringBuffer sb = new StringBuffer();
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new FileReader(input));
			String line = null;
			while((line = reader.readLine()) != null) {
				sb.append(line).append("\n");
			}
			return this.parseText(sb.toString());
		} finally {
			Util.close(reader);
		}
	}

	public Software parseText(String input) {
		LOG.info("parseText(input.length="+input.length()+")");
		
		final List<ICommand> commands = new LinkedList<ICommand>();
		final String[] lines = input.split("\\\n");
		int currentLine = 0;
		
		for (final String line : lines) {
			currentLine++;
			

			if(line.trim().length() == 0) {
				LOG.trace("skipping empty line #" + currentLine+": [" + line + "]");
				continue;
			} else if(line.startsWith("#")) {
				LOG.trace("skipping comment on line #" + currentLine+": [" + line + "]");
				continue;
			}
			
			LOG.trace("parsing line #" + currentLine+": [" + line + "]");
			
			final String[] lineParts = line.split(" ");
			if(lineParts.length != 3) {
				throw new RuntimeException("Invalid command on line " + currentLine + "!");
			}

			final String commandNamedId = lineParts[0];
			final int operand1 = Integer.parseInt(lineParts[2]); // A
			final int operand2 = Integer.parseInt(lineParts[1]); // B
			
			commands.add(this.commandFactory.newInstanceByName(commandNamedId, operand1, operand2));
		}
		return new Software(commands);
	}

	
	
	

	
	public ICommand parseBits(Register registerValue) {
		LOG.debug("parseBits(registerValue="+registerValue+")");
		
		final int commandId = extractCommandId(registerValue.getIntValue());
		final int operand1 = extractOperand1(registerValue.getIntValue());
		final int operand2 = extractOperand2(registerValue.getIntValue());
		
		return this.commandFactory.newInstanceById(commandId, operand1, operand2);
		
	}
	
	private static int extractCommandId(int registerValue) {
//		LOG.trace("extractCommandId(registerValue=" + registerValue + ")");
		final int result = Util.extractNumber(registerValue, CommandConstants.COMMAND_FROM, CommandConstants.COMMAND_TO);
//		LOG.trace("result="+result);
		return result;
	}
	
	private static int extractOperand1(int registerValue) {
		return Util.extractNumber(registerValue, CommandConstants.OPERAND1_FROM, CommandConstants.OPERAND1_TO);
	}
	
	private static int extractOperand2(int registerValue) {
		return Util.extractNumber(registerValue, CommandConstants.OPERAND2_FROM, CommandConstants.OPERAND2_TO);
	}
}
